package online.foxnull2.foxmall.product.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import online.foxnull2.foxmall.product.entity.CategoryEntity;
import online.foxnull2.foxmall.product.mapper.CategoryMapper;
import online.foxnull2.foxmall.product.service.CategoryBrandRelationService;
import online.foxnull2.foxmall.product.service.CategoryService;
import online.foxnull2.foxmall.product.utils.PageUtils;
import online.foxnull2.foxmall.product.utils.Query;
import online.foxnull2.foxmall.product.vo.Catalog2Vo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
import org.springframework.util.ObjectUtils;

import java.util.*;
import java.util.stream.Collectors;


@Service("categoryService")
public class CategoryServiceImpl extends ServiceImpl<CategoryMapper, CategoryEntity> implements CategoryService {

    private CategoryBrandRelationService categoryBrandRelationService;

    @Autowired
    public void setCategoryBrandRelationService(CategoryBrandRelationService categoryBrandRelationService) {
        this.categoryBrandRelationService = categoryBrandRelationService;
    }

    @Override
    public PageUtils queryPage(Map<String, Object> params) {
        IPage<CategoryEntity> page = this.page(
                new Query<CategoryEntity>().getPage(params),
                new QueryWrapper<>()
        );

        return new PageUtils(page);
    }

    @Override
    public PageUtils listWithTree() {
        List<CategoryEntity> categoryEntities = this.list();
        List<CategoryEntity> categoryEntitiesWithTree =
                categoryEntities.stream()
                        .filter(categoryEntity -> 0 == categoryEntity.getParentCid())
                        .map(categoryEntity -> categoryEntity.setChildren(getChildren(categoryEntity, categoryEntities)))
                        .sorted(Comparator.comparingInt(l -> (null == l.getSort() ? 0 : l.getSort())))
                        .collect(Collectors.toList());

        IPage<CategoryEntity> page = new Page<CategoryEntity>().setRecords(categoryEntitiesWithTree);

        return new PageUtils(page);
    }

    @Override
    public boolean ReRemoveByIds(List<Long> catIds) {
        //TODO rewrite removeByIds
        baseMapper.deleteBatchIds(catIds);
        return true;
    }

    @Override
    public List<Long> getCatalogIdPath(Long attrGroupId) {
        return rcsCatalogIdPath(attrGroupId);
    }

    @Override
    @CacheEvict(value = "category", allEntries = true)
    public Boolean updateCascade(CategoryEntity category) {
        this.updateById(category);
        if (!ObjectUtils.isEmpty(category.getName())) {
            categoryBrandRelationService.updateCategory(category.getCatId(), category.getName());
        }
        return null;
    }

    @Override
    @Cacheable(value = "category", key = "#root.methodName", sync = true)
    public Map<String, List<Catalog2Vo>> getJsonCatalog() {
        List<CategoryEntity> categoryEntityList = this.list();
        if (ObjectUtils.isEmpty(categoryEntityList)) {
            return null;
        }
        List<CategoryEntity> lv1CategoryList = getLevelCategoryByPid(categoryEntityList, 0L);
        return lv1CategoryList.stream().collect(Collectors.toMap(k -> k.getCatId().toString(), lv1 -> {
            List<CategoryEntity> lv2CategoryList = getLevelCategoryByPid(categoryEntityList, lv1.getCatId());
            List<Catalog2Vo> catalog2VoList = new ArrayList<>();
            if (!ObjectUtils.isEmpty(lv2CategoryList)) {
                catalog2VoList = lv2CategoryList.stream().map(lv2 -> {
                    Catalog2Vo catalog2Vo = new Catalog2Vo();
                    catalog2Vo
                            .setCatalog1Id(lv1.getCatId().toString())
                            .setId(lv2.getCatId().toString())
                            .setName(lv2.getName());
                    List<CategoryEntity> lv3CategoryList = getLevelCategoryByPid(categoryEntityList, lv2.getCatId());
                    if (!ObjectUtils.isEmpty(lv3CategoryList)) {
                        List<Catalog2Vo.Catalog3> catalog3List = lv3CategoryList.stream().map(lv3 -> {
                            Catalog2Vo.Catalog3 catalog3 = new Catalog2Vo.Catalog3();
                            catalog3
                                    .setCatalog2Id(lv2.getCatId().toString())
                                    .setId(lv3.getCatId().toString())
                                    .setName(lv3.getName());
                            return catalog3;
                        }).collect(Collectors.toList());
                        catalog2Vo.setCatalog3List(catalog3List);
                    }
                    return catalog2Vo;
                }).collect(Collectors.toList());
            }
            return catalog2VoList;
        }));
    }

    @Override
    @Cacheable(value = {"category"}, key = "#root.methodName", sync = true)
    public List<CategoryEntity> getLv1Category() {
        List<CategoryEntity> categoryEntityList = this.list();
        return getLevelCategoryByPid(categoryEntityList, 0L);
    }

    private List<CategoryEntity> getLevelCategoryByPid(List<CategoryEntity> categoryEntities, Long pid) {
        return categoryEntities.stream().filter(categoryEntity -> Objects.equals(categoryEntity.getParentCid(), pid)).collect(Collectors.toList());
    }

    //递归 Recursion
    private List<Long> rcsCatalogIdPath(Long catalogId) {
        List<Long> path = new ArrayList<>();
        path.add(0, catalogId);
        Long parentCid = this.getById(catalogId).getParentCid();
        if (0 != parentCid && !ObjectUtils.isEmpty(parentCid)) {
            List<Long> idPath = rcsCatalogIdPath(parentCid);
            path.addAll(0, idPath);
        }
        return path;
    }

    private List<CategoryEntity> getChildren(CategoryEntity lift, List<CategoryEntity> categoryEntities) {
        return categoryEntities.stream()
                .filter(categoryEntity -> lift.getCatId().equals(categoryEntity.getParentCid()))
                .map(categoryEntity -> categoryEntity.setChildren(getChildren(categoryEntity, categoryEntities)))
                .sorted(Comparator.comparingInt(l -> (null == l.getSort() ? 0 : l.getSort())))
                .collect(Collectors.toList());
    }

}